﻿using Microsoft.AspNetCore.Http;
using Single.AppCore;
using Single.Json;
using Single.YitId;
using SqlSugar;
using System.Collections.Concurrent;
using System.Security.Claims;
using System.Text;

namespace Single.Logger
{
    public static class LoggerHelper
    {
        public static ConcurrentQueue<LoggerQueueEntity> queue = new ConcurrentQueue<LoggerQueueEntity>();
        public static readonly ConnectionConfig options = GlobalApp.GetOptions<ConnectionConfig>("LoggerSettings:ConnectionString");

        private static SqlSugarScope loggerDb;

        static LoggerHelper()
        {
            options.ConfigureExternalServices = new ConfigureExternalServices
            {
                EntityService = (x, p) =>
                {
                    p.DbColumnName = UtilMethods.ToUnderLine(p.DbColumnName);
                },
                EntityNameService = (x, p) =>
                {
                    p.DbTableName = UtilMethods.ToUnderLine(p.DbTableName);
                }
            };

            loggerDb = new SqlSugarScope(options);

            Task.Run(() => { Start(); });
        }

        private static void Start()
        {
            List<LoggerEntity> baseList = new List<LoggerEntity>();
            List<LoggerRequest> requestList = new List<LoggerRequest>();

            while (true)
            {
                try
                {
                    if (queue.Count() > 0 && baseList.Count + requestList.Count < 500)
                    {
                        queue.TryDequeue(out LoggerQueueEntity? entity);

                        switch (entity.Type)
                        {
                            case nameof(LoggerEntity):
                                baseList.Add(entity.Entity as LoggerEntity);
                                break;
                            case nameof(LoggerRequest):
                                requestList.Add(entity.Entity as LoggerRequest);
                                break;
                            default: break;
                        }

                        continue;
                    }

                    //每1秒写一次数据
                    Thread.Sleep(2000);

                    if (baseList.Count > 0)
                    {
                        loggerDb.Fastest<LoggerEntity>().BulkCopy(baseList); baseList.Clear();
                    }

                    if (requestList.Count > 0)
                    {
                        loggerDb.Fastest<LoggerRequest>().BulkCopy(requestList); requestList.Clear();
                    }
                }
                catch (Exception ex)
                {
                    baseList.Clear(); requestList.Clear();

                    Console.WriteLine($"日志批量写入数据时出错:{ex.Message}");
                }
            }
        }

        public static void Info(string message)
        {
            Log(LoggerType.Info, null, message, null);
        }

        public static void Success(string message)
        {
            Log(LoggerType.Success, null, message, null);
        }

        public static void Error(string message)
        {
            Log(LoggerType.Error, null, message, null);
        }

        public static void Error(string message, Exception ex)
        {
            Log(LoggerType.Error, null, message, ex.StackTrace);
        }

        public static void Log(LoggerType type, string status, string message, string data)
        {
            Log(type.ToString(), status, message, data);
        }

        public static void Log(string type, string status, string message, string data)
        {
            Log(type, status, message, data, GlobalApp.User);
        }

        public static void Log(string type, string status, string message, string data, ClaimsPrincipal user)
        {
            var log = new LoggerEntity();

            log.Id = YitIdHelper.NextId();
            log.CreateTime = DateTime.Now;
            log.LogType = type;
            log.LogStatus = status;
            log.Message = message;
            log.Data = data;

            if (user != null && user.Identity.IsAuthenticated)
            {
                log.UserId = user.GetUserId();
                log.Username = user.GetUserName();
                log.Name = user.GetName();
            }

            queue.Enqueue(new LoggerQueueEntity(log));
        }

        public static async void Request(HttpContext context)
        {
            var request = context.Request;

            var log = new LoggerRequest();

            log.Id = YitIdHelper.NextId();
            log.CreateTime = DateTime.Now;
            log.Method = request.Method;
            log.Url = $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}{request.QueryString}";
            log.UserIp = context.GetUserIp();
            log.BrowserType = request.Headers["User-Agent"];
            log.ContentType = request.ContentType;
            log.RequestParameter = await request.GetRequestParameters();

            var user = context.User;

            if (user != null && user.Identity.IsAuthenticated)
            {
                log.UserId = user.GetUserId();
                log.Username = user.GetUserName();
                log.Name = user.GetName();
            }

            queue.Enqueue(new LoggerQueueEntity(log));
        }

        private static string GetUserIp(this HttpContext context)
        {
            string realIP = null, forwarded = null;
            string remoteIpAddress = context.Connection.RemoteIpAddress.ToString();
            if (context.Request.Headers.ContainsKey("X-Real-IP"))
            {
                realIP = context.Request.Headers["X-Real-IP"].ToString();
                if (realIP != remoteIpAddress) { remoteIpAddress = realIP; }
            }
            if (context.Request.Headers.ContainsKey("X-Forwarded-For"))
            {
                forwarded = context.Request.Headers["X-Forwarded-For"].ToString();
                if (forwarded != remoteIpAddress) { remoteIpAddress = forwarded; }
            }
            return remoteIpAddress?.Replace("::ffff:", "");
        }

        private static async Task<string> GetRequestParameters(this HttpRequest request)
        {
            string requestParameter = string.Empty;

            if (request.Method == "POST" && request.ContentType != null)
            {
                if (request.ContentType.ToLower().Contains("application/x-www-form-urlencoded") || request.ContentType.ToLower().Contains("multipart/form-data"))
                {
                    if (request.Form.Count > 0 || request.Form.Files.Count > 0)
                    {
                        var dic = new Dictionary<string, object>();

                        foreach (var key in request.Form.Keys)
                        {
                            var value = request.Form[key];

                            if (key == "password") { value = "******"; }

                            dic.Add(key, value);
                        }

                        foreach (var file in request.Form.Files)
                        {
                            dic.Add(file.Name, file);
                        }

                        requestParameter = JsonHelper.Serialize(dic, ignore: ["ContentDisposition", "Headers"]);
                    }
                }

                if (request.ContentType.ToLower().Contains("application/json") && request.Body != null && request.Body.CanRead)
                {
                    request.EnableBuffering();

                    using (StreamReader reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true))
                    {
                        requestParameter = await reader.ReadToEndAsync();

                        request.Body.Position = 0;
                    }
                }
            }

            return requestParameter;
        }
    }
}